#include <stdbool.h>

#include "py/runtime.h"
#include "py/gc.h"
#include "mod/machine.h"
#include "mod/fifo.h"
#include "mpxt804.h"
#include "mpxt804_board.h"

#include "shared/runtime/interrupt_char.h"


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 串口代码
//

static void _uart_init(UART_HandleTypeDef* huart, bool is_reset)
{
	__HAL_RCC_GPIO_CLK_ENABLE();
	
#if MICROPY_HW_EN_UART0
    UART_GPIO_CFG_CHECK(huart, 0, UART0_IRQn, is_reset);
#endif
#if MICROPY_HW_EN_UART1
    UART_GPIO_CFG_CHECK(huart, 1, UART1_IRQn, is_reset);
#endif
#if MICROPY_HW_EN_UART2
    UART_GPIO_CFG_CHECK(huart, 2, UART2_5_IRQn, is_reset);
#endif
#if MICROPY_HW_EN_UART3
    UART_GPIO_CFG_CHECK(huart, 3, UART2_5_IRQn, is_reset);
#endif
#if MICROPY_HW_EN_UART4
    UART_GPIO_CFG_CHECK(huart, 4, UART2_5_IRQn, is_reset);
#endif
#if MICROPY_HW_EN_UART5
    UART_GPIO_CFG_CHECK(huart, 5, UART2_5_IRQn, is_reset);
#endif
}

void HAL_UART_MspInit(UART_HandleTypeDef* huart)
{
    _uart_init(huart, false);
}

void HAL_UART_MspDeInit(UART_HandleTypeDef* huart)
{
    _uart_init(huart, true);
}

// 串口发送完成回调函数
void HAL_UART_TxCpltCallback(UART_HandleTypeDef *huart)
{
    UNUSED(huart);
	//printf("HAL_UART_TxCpltCallback.\n");
	//pyb_uart_obj_t* uart_obj = container_of(huart, pyb_uart_obj_t, huart);
}

// 串口接收完成回调函数(硬件FIFO收满、接受超时)
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{
	pyb_uart_obj_t* uart_obj = container_of(huart, pyb_uart_obj_t, huart);
    
    uint16_t remain = fifo_remain(&uart_obj->fifo);
    uint16_t copy_cnt = (huart->RxXferCount > remain) ? (remain) : (huart->RxXferCount); // 缓存不足时, 剩余的字节数丢弃
    // 按字节拷贝数据(环形队列)
    for(int i = 0; i < copy_cnt; i++){
        #if MICROPY_HW_REPL_UART
        if(uart_obj->uart_id == MICROPY_HW_UART_REPL && huart->pRxBuffPtr[i] == mp_interrupt_char){
            mp_sched_keyboard_interrupt();
        }else{
        #endif
            fifo_push(&uart_obj->fifo, huart->pRxBuffPtr[i]);
        #if MICROPY_HW_REPL_UART
        }
        #endif
    }
	//printf("HAL_UART_RxCpltCallback: q_addr:%p, copy_cnt:%d, front:%d, rear:%d\n", &uart_obj->q, copy_cnt, uart_obj->q.front, uart_obj->q.rear);
	UNUSED(huart);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Timer代码
//

void HAL_TIM_Base_MspInit(TIM_HandleTypeDef* htim_base)
{
	__HAL_RCC_TIM_CLK_ENABLE();
	HAL_NVIC_SetPriority(TIM_IRQn, MICROPY_HW_TIM_PRI);
	HAL_NVIC_EnableIRQ(TIM_IRQn);
}

void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base)
{
	// 所有TIM共用一个时钟和中断, 使用多路TIM, 在某一路DeInit时关闭时钟和中断,会影响其它TIM
    // __HAL_RCC_TIM_CLK_DISABLE();pwm.init(1, outmode=pwm.single, cntmode=pwm.edgedwn, reload=True, psc=39, period=249, pwminv=False)
    // HAL_NVIC_DisableIRQ(TIM_IRQn);
    __HAL_TIM_DISABLE_IT(htim_base);   // 不关闭时钟和中断, 只禁止timer的中断请求位
}

// 定时器回调(定时完成)
void HAL_TIM_Callback(TIM_HandleTypeDef *htim)
{
    machine_timer_obj_t* timer_obj = container_of(htim, machine_timer_obj_t, htim);
    //printf("judge callable:%d\n", mp_obj_is_callable(timer_obj->callback));

    if(mp_obj_is_callable(timer_obj->callback)){
    #if 0
        mp_sched_schedule(timer_obj->callback, timer_obj);  /* 要执行tim.stop()后, 才会触发调用py的回调函数, 弃用这个方案 */
    #else
        mp_sched_lock();                                    /* 这部分代码拷贝自ports/stm32/uart.c(isr那块) */
        // When executing code within a handler we must lock the GC to prevent
        // any memory allocations.  We must also catch any exceptions.
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_1(timer_obj->callback, MP_OBJ_FROM_PTR(timer_obj));
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            timer_obj->callback = mp_const_none;
            timer_obj->is_enabled = false;
            __HAL_TIM_DISABLE_IT(&timer_obj->htim);
            mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in Timer(%u) interrupt handler\n", timer_obj->timer_id);
            mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
        }
        gc_unlock();
        mp_sched_unlock();
    #endif
    }
	UNUSED(htim);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PWM
//

// init pwm module(gpio, set pwm_obj->stat)
static void _pwm_init(PWM_HandleTypeDef* hpwm, bool is_reset)
{
	__HAL_RCC_GPIO_CLK_ENABLE();
    machine_pwm_obj_t* pwm_obj = container_of(hpwm, machine_pwm_obj_t, hpwm[hpwm->Channel]);
    WRITE_BIT(pwm_obj->stat, hpwm->Channel, !is_reset);  // set independent(stat) bit
    
    //printf("_pwm_init, reset:%d, iscap:%d\n", is_reset, pwm_obj->iscap);
    
	switch(hpwm->Channel){
    #if MICROPY_HW_EN_PWM_CH0
    case 0:
        PWM_GPIO_CFG(hpwm, 0, is_reset, is_reset);   
       if(hpwm->Init.OutMode == PWM_OUT_MODE_5SYNC){
        #if MICROPY_HW_EN_PWM_CH1
            PWM_GPIO_CFG(hpwm, 1, 0, is_reset);
        #endif
        #if MICROPY_HW_EN_PWM_CH2
            PWM_GPIO_CFG(hpwm, 2, 0, is_reset);
        #endif
        #if MICROPY_HW_EN_PWM_CH3
            PWM_GPIO_CFG(hpwm, 3, 0, is_reset);
        #endif
        #if MICROPY_HW_EN_PWM_CH4            
            PWM_GPIO_CFG(hpwm, 4, 0, is_reset);
        #endif
            WRITE_BIT(pwm_obj->stat, 7, !is_reset);  // SYNC5
        }else if(hpwm->Init.OutMode == PWM_OUT_MODE_2SYNC || hpwm->Init.OutMode == PWM_OUT_MODE_2COMPLEMENTARY){  // PWM0 and PWM1
        #if MICROPY_HW_EN_PWM_CH1
            PWM_GPIO_CFG(hpwm, 1, 0, is_reset); 
        #endif
            WRITE_BIT(pwm_obj->stat, ((hpwm->Init.OutMode == PWM_OUT_MODE_2SYNC) ? (5) : (8)), !is_reset);  // SYNC2(bit5), COMP2(bit8)
        }
        break;
    #endif
    
    #if MICROPY_HW_EN_PWM_CH1
    case 1:
        PWM_GPIO_CFG(hpwm, 1, is_reset, is_reset);
        break;
    #endif
    
    #if MICROPY_HW_EN_PWM_CH2
    case 2:
        PWM_GPIO_CFG(hpwm, 2, is_reset, is_reset);
        if(hpwm->Init.OutMode == PWM_OUT_MODE_2SYNC || hpwm->Init.OutMode == PWM_OUT_MODE_2COMPLEMENTARY){
        #if MICROPY_HW_EN_PWM_CH3
            PWM_GPIO_CFG(hpwm, 3, 0, is_reset);
        #endif
            WRITE_BIT(pwm_obj->stat, ((hpwm->Init.OutMode == PWM_OUT_MODE_2SYNC) ? (6) : (9)), !is_reset);  // SYNC2, COMP2
        }
        break;
    #endif

    #if MICROPY_HW_EN_PWM_CH3    
    case 3:
        PWM_GPIO_CFG(hpwm, 3, is_reset, is_reset);
        break;
    #endif
    
    #if MICROPY_HW_EN_PWM_CH4
    case 4:
        PWM_GPIO_CFG(hpwm, 4, is_reset, is_reset);
        break;
    #endif
    }
    // enable pwm interrupt
    //HAL_NVIC_SetPriority(PWM_IRQn, MICROPY_HW_PWM_PRI);
    //HAL_NVIC_EnableIRQ(PWM_IRQn);
}
    
void HAL_PWM_MspInit(PWM_HandleTypeDef *hpwm)
{
    __HAL_RCC_PWM_CLK_ENABLE();
	_pwm_init(hpwm, false);
}

void HAL_PWM_MspDeInit(PWM_HandleTypeDef *hpwm)
{
    machine_pwm_obj_t* pwm_obj = container_of(hpwm, machine_pwm_obj_t, hpwm[hpwm->Channel]);  
    // deinit pwm gpio
    for(int i = 0; i < MICROPY_HW_MAX_PWM_CNT; i++){
        if(pwm_obj->stat & (1<<i) && (hpwm->Channel == i || pwm_obj->reset_module)){ // reset hpwm->Channel, or all pwm channel
            // deinit gpio and reset stat bit
            _pwm_init(pwm_obj->hpwm + i, true);
            // stop dma transfer(cap0, cap1)
            if((i == 0 || i == 4) && (pwm_obj->stat & 1 << (10+i/4))){
                HAL_DMA_Abort(&pwm_obj->cap[i>>2].hdma);
                WRITE_BIT(pwm_obj->stat, 10+i/4, 0);
            }
        }
    }
    // no any channel used, shutdown pwm clock
    if(!(pwm_obj->stat & 0x1F) || pwm_obj->reset_module){
        pwm_obj->reset_module = false;
        pwm_obj->stat = 0;
        __HAL_RCC_PWM_CLK_DISABLE();
    }
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// RTC
//
void HAL_PMU_MspInit(PMU_HandleTypeDef *hpmu)
{
	HAL_NVIC_EnableIRQ(PMU_IRQn);
}

void HAL_PMU_MspDeInit(PMU_HandleTypeDef *hpmu)
{
	HAL_NVIC_DisableIRQ(PMU_IRQn);
}

void HAL_PMU_RTC_Callback(PMU_HandleTypeDef *hpmu)
{    
    machine_rtc_obj_t* rtc_obj = container_of(hpmu, machine_rtc_obj_t, hpmu);
    if(mp_obj_is_callable(rtc_obj->callback)){
    #if 1
        mp_sched_schedule(rtc_obj->callback, rtc_obj);
    #else                                                       /* copy from ports/stm32/uart.c(isr handler) */
        mp_sched_lock(); 
        // When executing code within a handler we must lock the GC to prevent
        // any memory allocations.  We must also catch any exceptions.
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_1(rtc_obj->callback, MP_OBJ_FROM_PTR(rtc_obj));
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            rtc_obj->callback = mp_const_none;
            HAL_PMU_RTC_Alarm_Disable(&rtc_obj->hpmu);
            mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in rtc interrupt handler\n");
            mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
        }
        gc_unlock();
        mp_sched_unlock();
    #endif
    }
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// GPIO-EXTI
//

// HAL_GPIO_EXTI_IRQHandler call this func internally
//void HAL_GPIO_EXTI_Callback(GPIO_TypeDef *GPIOx, uint32_t GPIO_Pin)
//{
//	UNUSED(GPIO_Pin);
//}

void GPIO_EXTI_Callback(uint32_t port_nr, uint32_t pin_nr)
{
    machine_pin_obj_t* pin = MP_STATE_PORT(pin_obj_all)[port_nr*16+pin_nr];
    //printf("exti callback, pin:%p, pin->callback:%p, is_func:%d.\n", pin, pin->callback, mp_obj_is_callable(pin->callback));
    if(pin && pin->callback && mp_obj_is_callable(pin->callback)){
    #if 0
        mp_sched_schedule(pin->callback, pin);
    #else                                                       /* copy from ports/stm32/uart.c(isr handler) */
        mp_sched_lock(); 
        // When executing code within a handler we must lock the GC to prevent
        // any memory allocations.  We must also catch any exceptions.
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_1(pin->callback, MP_OBJ_FROM_PTR(pin));
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            pin->callback = mp_const_none;
            mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in exti interrupt handler\n");
            mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
        }
        gc_unlock();
        mp_sched_unlock();
    #endif
    }
    
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// read flash ID
//

//  读取flash id,函数返回读取的字节数, 数组uuid必须大于等于20字节
int HAL_FLASH_ReadID(uint8_t *uuid, uint8_t len)
{
	uint8_t dummy_bytes = 0;          
	uint8_t uni_bytes = 0;
	
	if(!uuid || len < 0)
		return -1;
	
	// 读取JEDEC分配的厂家号(PUYA-0x85, Winbond-0xEF)
	FLASH->CMD_INFO = 0x2c09F;
    FLASH->CMD_START = FLASH_CMD_START_CMD;
	uint8_t rid = M32(RSA_BASE) & 0xFF;
	
	switch(rid){
	case 0xC8:  // SPIFLASH_MID_GD
	case 0x85:  // SPIFLASH_MID_PUYA
	case 0xEB:  // SPIFLASH_MID_TSINGTENG
	    dummy_bytes = 4;
		uni_bytes = 16;
        break;
		
	case 0xEF:  // SPIFLASH_MID_WINBOND
	case 0xA1:  // SPIFLASH_MID_FUDANMICRO
	case 0x68:  // SPIFLASH_MID_BOYA
	case 0x20:  // SPIFLASH_MID_XMC
	    dummy_bytes = 4;
		uni_bytes = 8;
        break;
		
	case 0x1C:  // SPIFLASH_MID_ESMT
	case 0x0B:  // SPIFLASH_MID_XTX
	default:
	    return -1;
	}
	
	uint8_t bytescnt = dummy_bytes + uni_bytes;
	uint8_t buffer[dummy_bytes + uni_bytes + 4];
	// 发送命令, 读取flash ID
	FLASH->CMD_INFO =  0xC04B|((bytescnt-1) << 16);
    FLASH->CMD_START = FLASH_CMD_START_CMD;
	// 跳过dummyByte(4B), 最后多读4Byte, 避免bytescnt不是4字节对齐, 额外多读一次
    for(int i = 0; i < bytescnt / 4; i++ ){
		M32(buffer + i * 4) = M32(RSA_BASE + (i + 1) * 4);
	}
	memcpy(uuid, buffer, (len >= uni_bytes) ? (uni_bytes) : (len));
	return (len >= uni_bytes) ? (uni_bytes) : (len);
}

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SPI
//

// SPI硬件初始化函数
void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi)
{
    machine_hard_spi_obj_t* self = container_of(hspi, machine_hard_spi_obj_t, hspi);
    self->dmastat = 0;
    __HAL_RCC_SPI_CLK_ENABLE();
    __HAL_RCC_DMA_CLK_ENABLE();

    //__HAL_AFIO_REMAP_SPI_CS(MICROPY_HW_PORT(MICROPY_HW_LSPI_CS), MICROPY_HW_PIN(MICROPY_HW_LSPI_CS));
    __HAL_AFIO_REMAP_SPI_CLK(MICROPY_HW_PORT(MICROPY_HW_LSPI_SCK), MICROPY_HW_PIN(MICROPY_HW_LSPI_SCK));
    __HAL_AFIO_REMAP_SPI_MISO(MICROPY_HW_PORT(MICROPY_HW_LSPI_MISO), MICROPY_HW_PIN(MICROPY_HW_LSPI_MISO));
    __HAL_AFIO_REMAP_SPI_MOSI(MICROPY_HW_PORT(MICROPY_HW_LSPI_MOSI), MICROPY_HW_PIN(MICROPY_HW_LSPI_MOSI));

	// enable spi interrupt
	HAL_NVIC_SetPriority(SPI_LS_IRQn, MICROPY_HW_LSPI_PRI);
	HAL_NVIC_EnableIRQ(SPI_LS_IRQn);

    // enable dma interrupt
    HAL_NVIC_SetPriority(DMA_IRQn(MICROPY_HW_LSPI_DMA), MICROPY_HW_LSPI_DMA_PRI);
	HAL_NVIC_EnableIRQ(DMA_IRQn(MICROPY_HW_LSPI_DMA));
}

// SPI硬件重置函数
void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi)
{
    machine_hard_spi_obj_t* self = container_of(hspi, machine_hard_spi_obj_t, hspi);

	__HAL_RCC_SPI_CLK_DISABLE();
    //HAL_GPIO_DeInit(MICROPY_HW_PORT(MICROPY_HW_SPI_CS), MICROPY_HW_PIN(MICROPY_HW_SPI_CS));
    //GPIO_AF_DISABLE(MICROPY_HW_SPI_CS);

    HAL_GPIO_DeInit(MICROPY_HW_PORT(MICROPY_HW_LSPI_SCK), MICROPY_HW_PIN(MICROPY_HW_LSPI_SCK));
    GPIO_AF_DISABLE(MICROPY_HW_LSPI_SCK);

    HAL_GPIO_DeInit(MICROPY_HW_PORT(MICROPY_HW_LSPI_MISO), MICROPY_HW_PIN(MICROPY_HW_LSPI_MISO));
    GPIO_AF_DISABLE(MICROPY_HW_LSPI_MISO);

    HAL_GPIO_DeInit(MICROPY_HW_PORT(MICROPY_HW_LSPI_MOSI), MICROPY_HW_PIN(MICROPY_HW_LSPI_MOSI));
    GPIO_AF_DISABLE(MICROPY_HW_LSPI_MOSI);

    self->dmastat = 0;
    HAL_NVIC_DisableIRQ(SPI_LS_IRQn);
    HAL_NVIC_DisableIRQ(DMA_IRQn(MICROPY_HW_LSPI_DMA));
}

// SPI收发函数(DMA版本), 必须满足4字节对齐, 不然会有问题(小端发送没问题, 接收可能会越界)
// 函数返回0表示正常, 返回负数表示出错
int spi_xfer_dma(SPI_HandleTypeDef* hspi, uint8_t buf[], int len, bool isread)
{
    machine_hard_spi_obj_t* self = container_of(hspi, machine_hard_spi_obj_t, hspi);

    // 传输的字节数必须满足4字节对齐
    if(len & 0x03){
        mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("SPI() xfer_dma(len=%d) failed, len must be aligned to sizeof(uint32_t)"), len);
        __HAL_LINKDMA(&self->hspi, hdmarx, self->hdma);
    }
    // 配置DMA控制结构
    if(isread){
        DMA_TAG_FILL(&self->hdma, MICROPY_HW_LSPI_DMA, DMA_PERIPH_TO_MEMORY, DMA_DINC_ENABLE, DMA_SINC_DISABLE, DMA_DATAALIGN_WORD, DMA_MODE_NORMAL_SINGLE, DMA_REQUEST_SOURCE_SPI_RX);
    }else{
        DMA_TAG_FILL(&self->hdma, MICROPY_HW_LSPI_DMA, DMA_MEMORY_TO_PERIPH, DMA_DINC_DISABLE, DMA_SINC_ENABLE, DMA_DATAALIGN_WORD, DMA_MODE_NORMAL_SINGLE, DMA_REQUEST_SOURCE_SPI_TX);
	    __HAL_LINKDMA(&self->hspi, hdmatx, self->hdma);
    }
	// 初始化DMA
	if(HAL_DMA_Init(&self->hdma) != HAL_OK){
		printf("config dma failed.\n");
		return -10;
	}
    if(isread){
        return -HAL_SPI_Receive_DMA(&self->hspi, buf, len);
    }else{
        return -HAL_SPI_Transmit_DMA(&self->hspi, buf, len);
    }
}

// 发送完成回调
void HAL_SPI_RxCpltCallback(SPI_HandleTypeDef* hspi)
{
    machine_hard_spi_obj_t* self = MP_STATE_PORT(spi_obj);

    if(self && self->callback && mp_obj_is_callable(self->callback)){
    #if 0
        mp_sched_schedule(self->callback, self);
    #else                 
        /* copy from ports/stm32/uart.c(isr handler) */
        mp_sched_lock(); 
        // When executing code within a handler we must lock the GC to prevent
        // any memory allocations.  We must also catch any exceptions.
        gc_lock();
        nlr_buf_t nlr;
        if (nlr_push(&nlr) == 0) {
            mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self));
            nlr_pop();
        } else {
            // Uncaught exception; disable the callback so it doesn't run again.
            self->callback = mp_const_none;
            mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in exti interrupt handler\n");
            mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val));
        }
        gc_unlock();
        mp_sched_unlock();
    #endif
    }
}

// 接收完成回调
void HAL_SPI_TxCpltCallback(SPI_HandleTypeDef* hspi)
{
    HAL_SPI_RxCpltCallback(hspi);
	//__HAL_SPI_SET_CS_HIGH(hspi);
	//printf("tx cplt\r\n");
}





/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// I2C
//



/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// WDT
//


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// SDIO
//


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// I2S
//

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// PMU
//


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 
//





